package com.yeziji.security.service;


import cn.hutool.core.util.StrUtil;
import com.google.common.io.BaseEncoding;
import com.yeziji.security.common.mfa.SecurityMfaInfo;
import com.yeziji.security.utils.MfaUtils;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * mfa 校验处理器
 *
 * @author hwy
 * @since 2024/11/18 22:06
 **/
public abstract class MfaValidateHandler {
    /**
     * 获取 mfa 的公司/前缀标记
     *
     * @return {@link String}
     */
    public abstract String getIssuer();

    /**
     * 获取用户与 MFA 密钥的键值对
     *
     * @return {@link Map}: {key: user object flag, value: mfa secret key}
     */
    public abstract Map<String, SecurityMfaInfo> getAsUserMfaSecretKeyMap();

    /**
     * 校验用户输入的 mfa 验证码
     *
     * @param userFlag 用户标识
     * @param input 用户输入的内容
     * @return {@link Boolean} 校验结果
     */
    public boolean validate(String userFlag, String input) {
        return Objects.equals(this.generateTOTP(userFlag), input);
    }

    /**
     * 生成 otp path 连接
     *
     * @param secret 用户对应的 secret key
     * @return {@link String} otp path 连接
     */
    public String generateOtpPath(String userFlag) {
        SecurityMfaInfo mfaInfo = this.getAsMfaInfo(userFlag);
        return MfaUtils.generateOtpPath(this.getIssuer(), mfaInfo.getAccountName(), mfaInfo.getSecureKey());
    }

    /**
     * 生成 totp 密钥
     *
     * @param userFlag 用户标识
     * @return {@link String} totp 密钥
     */
    public String generateTOTP(String userFlag) {
        SecurityMfaInfo mfaInfo = this.getAsMfaInfo(userFlag);
        if (StrUtil.isBlank(mfaInfo.getSecureKey())) {
            throw new IllegalArgumentException("找不到当前用户的键值对!!!");
        }
        return MfaUtils.generateTOTP(this.getAsBase32Key(mfaInfo.getSecureKey()));
    }

    public SecurityMfaInfo getAsMfaInfo(String userFlag) {
        try {
            return this.getAsUserMfaSecretKeyMap().get(userFlag);
        } catch (Exception ignored) {
            return null;
        }
    }

    /**
     * 获取 base32 封装的 key
     *
     * @param secretKey 待封装 key
     * @return {@link String} base32 Key
     */
    protected String getAsBase32Key(String secretKey) {
        return Optional.ofNullable(secretKey).map(key -> BaseEncoding.base32().encode(key.getBytes())).map(String::new).orElseThrow(() -> new IllegalArgumentException("not found encode key"));
    }
}
